// *NEW: Include standard c++ headers
#include <iostream>
#include <fstream>

/*#ifndef __included_gl_glew
#define __included_gl_glew	1
#include <GL/glew.h>
#endif*/
#include <GL3/gl3w.h>
//#include <GL3/gl3.h>

// *NEW: Include SDL (Rather than glfw)
#include <SDL/SDL.h>
//#include <SDL/SDL_opengl.h>

// Include GLM
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

#include "shader.hpp"
#include "texture.hpp"
#include "controls.hpp"
#include "sdlapp.hpp"

// *NEW: Make sure main has the proper argument handling (otherwise it will conflict with SDLmain)
int main( int argc, char *argv[] )
{
	std::ofstream log( "log.txt" );
	try {
	
		log<<"Initializing SDL Application...";
		int glVersion[2] = {3, 1};
		SDLApp application( "Tutorial 06", 1024, 600, 16, glVersion, true, false, false );
		log<<"done"<<std::endl;
		
		application.setMouse(-1, -1);	// In the SDLApp class, setting -1 for an x or y puts the mouse at half resolution of that plane (x, or y)

		// Dark blue background
		glClearColor(0.0f, 0.0f, 0.3f, 0.0f);

		// Enable depth test
		glEnable(GL_DEPTH_TEST);
		// Accept fragment if it closer to the camera than the former one
		glDepthFunc(GL_LESS); 

		// Cull triangles which normal is not towards the camera
		//glEnable(GL_CULL_FACE);

		GLuint VertexArrayID;
		glGenVertexArrays(1, &VertexArrayID);
		glBindVertexArray(VertexArrayID);
		
		// Create and compile our GLSL program from the shaders
		GLuint programID = LoadShaders( "TransformVertexShader.vertexshader", "TextureFragmentShader.fragmentshader" );

		// Get a handle for our "MVP" uniform
		GLuint MatrixID = glGetUniformLocation(programID, "MVP");

		// Load the texture
		GLuint Texture = loadTexture("uvtemplate.tga");
		
		// Get a handle for our "myTextureSampler" uniform
		GLuint TextureID  = glGetUniformLocation(programID, "myTextureSampler");

		// Our vertices. Tree consecutive floats give a 3D vertex; Three consecutive vertices give a triangle.
		// A cube has 6 faces with 2 triangles each, so this makes 6*2=12 triangles, and 12*3 vertices
		static const GLfloat g_vertex_buffer_data[] = { 
			-1.0f,-1.0f,-1.0f,
			-1.0f,-1.0f, 1.0f,
			-1.0f, 1.0f, 1.0f,
			 1.0f, 1.0f,-1.0f,
			-1.0f,-1.0f,-1.0f,
			-1.0f, 1.0f,-1.0f,
			 1.0f,-1.0f, 1.0f,
			-1.0f,-1.0f,-1.0f,
			 1.0f,-1.0f,-1.0f,
			 1.0f, 1.0f,-1.0f,
			 1.0f,-1.0f,-1.0f,
			-1.0f,-1.0f,-1.0f,
			-1.0f,-1.0f,-1.0f,
			-1.0f, 1.0f, 1.0f,
			-1.0f, 1.0f,-1.0f,
			 1.0f,-1.0f, 1.0f,
			-1.0f,-1.0f, 1.0f,
			-1.0f,-1.0f,-1.0f,
			-1.0f, 1.0f, 1.0f,
			-1.0f,-1.0f, 1.0f,
			 1.0f,-1.0f, 1.0f,
			 1.0f, 1.0f, 1.0f,
			 1.0f,-1.0f,-1.0f,
			 1.0f, 1.0f,-1.0f,
			 1.0f,-1.0f,-1.0f,
			 1.0f, 1.0f, 1.0f,
			 1.0f,-1.0f, 1.0f,
			 1.0f, 1.0f, 1.0f,
			 1.0f, 1.0f,-1.0f,
			-1.0f, 1.0f,-1.0f,
			 1.0f, 1.0f, 1.0f,
			-1.0f, 1.0f,-1.0f,
			-1.0f, 1.0f, 1.0f,
			 1.0f, 1.0f, 1.0f,
			-1.0f, 1.0f, 1.0f,
			 1.0f,-1.0f, 1.0f
		};

		// Two UV coordinatesfor each vertex. They were created with Blender.
		static const GLfloat g_uv_buffer_data[] = { 
			0.000059f, 1.0f-0.000004f, 
			0.000103f, 1.0f-0.336048f, 
			0.335973f, 1.0f-0.335903f, 
			1.000023f, 1.0f-0.000013f, 
			0.667979f, 1.0f-0.335851f, 
			0.999958f, 1.0f-0.336064f, 
			0.667979f, 1.0f-0.335851f, 
			0.336024f, 1.0f-0.671877f, 
			0.667969f, 1.0f-0.671889f, 
			1.000023f, 1.0f-0.000013f, 
			0.668104f, 1.0f-0.000013f, 
			0.667979f, 1.0f-0.335851f, 
			0.000059f, 1.0f-0.000004f, 
			0.335973f, 1.0f-0.335903f, 
			0.336098f, 1.0f-0.000071f, 
			0.667979f, 1.0f-0.335851f, 
			0.335973f, 1.0f-0.335903f, 
			0.336024f, 1.0f-0.671877f, 
			1.000004f, 1.0f-0.671847f, 
			0.999958f, 1.0f-0.336064f, 
			0.667979f, 1.0f-0.335851f, 
			0.668104f, 1.0f-0.000013f, 
			0.335973f, 1.0f-0.335903f, 
			0.667979f, 1.0f-0.335851f, 
			0.335973f, 1.0f-0.335903f, 
			0.668104f, 1.0f-0.000013f, 
			0.336098f, 1.0f-0.000071f, 
			0.000103f, 1.0f-0.336048f, 
			0.000004f, 1.0f-0.671870f, 
			0.336024f, 1.0f-0.671877f, 
			0.000103f, 1.0f-0.336048f, 
			0.336024f, 1.0f-0.671877f, 
			0.335973f, 1.0f-0.335903f, 
			0.667969f, 1.0f-0.671889f, 
			1.000004f, 1.0f-0.671847f, 
			0.667979f, 1.0f-0.335851f
		};

		GLuint vertexbuffer;
		glGenBuffers(1, &vertexbuffer);
		glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
		glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

		GLuint uvbuffer;
		glGenBuffers(1, &uvbuffer);
		glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
		glBufferData(GL_ARRAY_BUFFER, sizeof(g_uv_buffer_data), g_uv_buffer_data, GL_STATIC_DRAW);

		while( application.keepRunning() ) {

			// Clear the screen
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

			// Use our shader
			glUseProgram(programID);

			// Compute the MVP matrix from keyboard and mouse input
			computeMatricesFromInputs( application );
			glm::mat4 ProjectionMatrix = getProjectionMatrix();
			glm::mat4 ViewMatrix = getViewMatrix();
			glm::mat4 ModelMatrix = glm::mat4(1.0);
			glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix;

			// Send our transformation to the currently bound shader, 
			// in the "MVP" uniform
			glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &MVP[0][0]);

			// Bind our texture in Texture Unit 0
			glActiveTexture(GL_TEXTURE0);
			glBindTexture(GL_TEXTURE_2D, Texture);
			// Set our "myTextureSampler" sampler to user Texture Unit 0
			glUniform1i(TextureID, 0);

			// 1rst attribute buffer : vertices
			glEnableVertexAttribArray(0);
			glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
			glVertexAttribPointer(
				0,                  // attribute. No particular reason for 0, but must match the layout in the shader.
				3,                  // size
				GL_FLOAT,           // type
				GL_FALSE,           // normalized?
				0,                  // stride
				(void*)0            // array buffer offset
			);

			// 2nd attribute buffer : UVs
			glEnableVertexAttribArray(1);
			glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
			glVertexAttribPointer(
				1,                                // attribute. No particular reason for 1, but must match the layout in the shader.
				2,                                // size : U+V => 2
				GL_FLOAT,                         // type
				GL_FALSE,                         // normalized?
				0,                                // stride
				(void*)0                          // array buffer offset
			);

			// Draw the triangle !
			glDrawArrays(GL_TRIANGLES, 0, 12*3); // From index 0 to 12*3 -> 12 triangles

			glDisableVertexAttribArray(0);
			glDisableVertexAttribArray(1);

			// *New: Gather and process input
			application.handleInput( );
			
			//*New: Swap buffers (The SDLApp way)
			application.updateVideo( );
			//SDL_Delay(10);

		}

		// Cleanup VBO and shader
		glDeleteBuffers(1, &vertexbuffer);
		glDeleteBuffers(1, &uvbuffer);
		glDeleteProgram(programID);
		glDeleteTextures(1, &TextureID);

   } catch( const SDLexc& err ) {
		log<<"SDL Error: "<<err.what()<<std::endl;
		return 1;
	} catch( const std::string& strerror ) {
		log<<"Error: "<<strerror<<std::endl;
		return 2;
	}
	
	return 0;
}

